/* *
 *
 *  (c) 2009-2019 Øystein Moseng
 *
 *  Accessibility component for exporting menu.
 *
 *  License: www.highcharts.com/license
 *
 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
 *
 * */
"use strict";
import H from "../../../parts/Globals.js";
import U from "../../../parts/Utilities.js";
var extend = U.extend;
import AccessibilityComponent from "../AccessibilityComponent.js";
import KeyboardNavigationHandler from "../KeyboardNavigationHandler.js";
import ChartUtilities from "../utils/chartUtilities.js";
var unhideChartElementFromAT = ChartUtilities.unhideChartElementFromAT;
import HTMLUtilities from "../utils/htmlUtilities.js";
var removeElement = HTMLUtilities.removeElement,
  getFakeMouseEvent = HTMLUtilities.getFakeMouseEvent;
/* eslint-disable no-invalid-this, valid-jsdoc */
/**
 * Show the export menu and focus the first item (if exists).
 *
 * @private
 * @function Highcharts.Chart#showExportMenu
 */
H.Chart.prototype.showExportMenu = function () {
  if (this.exportSVGElements && this.exportSVGElements[0]) {
    var el = this.exportSVGElements[0].element;
    if (el.onclick) {
      el.onclick(getFakeMouseEvent("click"));
    }
  }
};
/**
 * @private
 * @function Highcharts.Chart#hideExportMenu
 */
H.Chart.prototype.hideExportMenu = function () {
  var chart = this,
    exportList = chart.exportDivElements;
  if (exportList && chart.exportContextMenu) {
    // Reset hover states etc.
    exportList.forEach(function (el) {
      if (el.className === "highcharts-menu-item" && el.onmouseout) {
        el.onmouseout(getFakeMouseEvent("mouseout"));
      }
    });
    chart.highlightedExportItemIx = 0;
    // Hide the menu div
    chart.exportContextMenu.hideMenu();
    // Make sure the chart has focus and can capture keyboard events
    chart.container.focus();
  }
};
/**
 * Highlight export menu item by index.
 *
 * @private
 * @function Highcharts.Chart#highlightExportItem
 *
 * @param {number} ix
 *
 * @return {boolean}
 */
H.Chart.prototype.highlightExportItem = function (ix) {
  var listItem = this.exportDivElements && this.exportDivElements[ix],
    curHighlighted =
      this.exportDivElements &&
      this.exportDivElements[this.highlightedExportItemIx],
    hasSVGFocusSupport;
  if (
    listItem &&
    listItem.tagName === "LI" &&
    !(listItem.children && listItem.children.length)
  ) {
    // Test if we have focus support for SVG elements
    hasSVGFocusSupport = !!(this.renderTo.getElementsByTagName("g")[0] || {})
      .focus;
    // Only focus if we can set focus back to the elements after
    // destroying the menu (#7422)
    if (listItem.focus && hasSVGFocusSupport) {
      listItem.focus();
    }
    if (curHighlighted && curHighlighted.onmouseout) {
      curHighlighted.onmouseout(getFakeMouseEvent("mouseout"));
    }
    if (listItem.onmouseover) {
      listItem.onmouseover(getFakeMouseEvent("mouseover"));
    }
    this.highlightedExportItemIx = ix;
    return true;
  }
  return false;
};
/**
 * Try to highlight the last valid export menu item.
 *
 * @private
 * @function Highcharts.Chart#highlightLastExportItem
 * @return {boolean}
 */
H.Chart.prototype.highlightLastExportItem = function () {
  var chart = this,
    i;
  if (chart.exportDivElements) {
    i = chart.exportDivElements.length;
    while (i--) {
      if (chart.highlightExportItem(i)) {
        return true;
      }
    }
  }
  return false;
};
/**
 * @private
 * @param {Highcharts.Chart} chart
 */
function exportingShouldHaveA11y(chart) {
  var exportingOpts = chart.options.exporting;
  return !!(
    exportingOpts &&
    exportingOpts.enabled !== false &&
    exportingOpts.accessibility &&
    exportingOpts.accessibility.enabled &&
    chart.exportSVGElements &&
    chart.exportSVGElements[0] &&
    chart.exportSVGElements[0].element
  );
}
/**
 * The MenuComponent class
 *
 * @private
 * @class
 * @name Highcharts.MenuComponent
 */
var MenuComponent = function () {};
MenuComponent.prototype = new AccessibilityComponent();
extend(
  MenuComponent.prototype,
  /** @lends Highcharts.MenuComponent */ {
    /**
     * Init the component
     */
    init: function () {
      var chart = this.chart,
        component = this;
      this.addEvent(chart, "exportMenuShown", function () {
        component.onMenuShown();
      });
      this.addEvent(chart, "exportMenuHidden", function () {
        component.onMenuHidden();
      });
    },
    /**
     * @private
     */
    onMenuHidden: function () {
      var menu = this.chart.exportContextMenu;
      if (menu) {
        menu.setAttribute("aria-hidden", "true");
      }
      this.setExportButtonExpandedState("false");
    },
    /**
     * @private
     */
    onMenuShown: function () {
      var chart = this.chart,
        menu = chart.exportContextMenu;
      if (menu) {
        this.addAccessibleContextMenuAttribs();
        unhideChartElementFromAT(chart, menu);
      }
      this.setExportButtonExpandedState("true");
    },
    /**
     * @private
     * @param {string} stateStr
     */
    setExportButtonExpandedState: function (stateStr) {
      var button = this.exportButtonProxy;
      if (button) {
        button.setAttribute("aria-expanded", stateStr);
      }
    },
    /**
     * Called on each render of the chart. We need to update positioning of the
     * proxy overlay.
     */
    onChartRender: function () {
      var chart = this.chart,
        a11yOptions = chart.options.accessibility;
      // Always start with a clean slate
      removeElement(this.exportProxyGroup);
      // Set screen reader properties on export menu
      if (exportingShouldHaveA11y(chart)) {
        // Proxy button and group
        this.exportProxyGroup = this.addProxyGroup(
          // Wrap in a region div if verbosity is high
          a11yOptions.landmarkVerbosity === "all"
            ? {
                "aria-label": chart.langFormat(
                  "accessibility.exporting.exportRegionLabel",
                  { chart: chart }
                ),
                role: "region",
              }
            : {}
        );
        var button = this.chart.exportSVGElements[0];
        this.exportButtonProxy = this.createProxyButton(
          button,
          this.exportProxyGroup,
          {
            "aria-label": chart.langFormat(
              "accessibility.exporting.menuButtonLabel",
              { chart: chart }
            ),
            "aria-expanded": "false",
          }
        );
      }
    },
    /**
     * @private
     */
    addAccessibleContextMenuAttribs: function () {
      var chart = this.chart,
        exportList = chart.exportDivElements;
      if (exportList && exportList.length) {
        // Set tabindex on the menu items to allow focusing by script
        // Set role to give screen readers a chance to pick up the contents
        exportList.forEach(function (item) {
          if (
            item.tagName === "LI" &&
            !(item.children && item.children.length)
          ) {
            item.setAttribute("tabindex", -1);
          } else {
            item.setAttribute("aria-hidden", "true");
          }
        });
        // Set accessibility properties on parent div
        var parentDiv = exportList[0].parentNode;
        parentDiv.removeAttribute("aria-hidden");
        parentDiv.setAttribute(
          "aria-label",
          chart.langFormat("accessibility.exporting.chartMenuLabel", {
            chart: chart,
          })
        );
      }
    },
    /**
     * Get keyboard navigation handler for this component.
     * @return {Highcharts.KeyboardNavigationHandler}
     */
    getKeyboardNavigation: function () {
      var keys = this.keyCodes,
        chart = this.chart,
        component = this;
      return new KeyboardNavigationHandler(chart, {
        keyCodeMap: [
          // Arrow prev handler
          [
            [keys.left, keys.up],
            function () {
              return component.onKbdPrevious(this);
            },
          ],
          // Arrow next handler
          [
            [keys.right, keys.down],
            function () {
              return component.onKbdNext(this);
            },
          ],
          // Click handler
          [
            [keys.enter, keys.space],
            function () {
              return component.onKbdClick(this);
            },
          ],
          // ESC handler
          [
            [keys.esc],
            function () {
              return this.response.prev;
            },
          ],
        ],
        // Only run exporting navigation if exporting support exists and is
        // enabled on chart
        validate: function () {
          return (
            chart.exportChart &&
            chart.options.exporting.enabled !== false &&
            chart.options.exporting.accessibility.enabled !== false
          );
        },
        // Show export menu
        init: function (direction) {
          chart.showExportMenu();
          if (direction < 0) {
            chart.highlightLastExportItem();
          } else {
            chart.highlightExportItem(0);
          }
        },
        // Hide the menu
        terminate: function () {
          chart.hideExportMenu();
        },
      });
    },
    /**
     * @private
     * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
     * @return {number}
     * Response code
     */
    onKbdPrevious: function (keyboardNavigationHandler) {
      var chart = this.chart,
        a11yOptions = chart.options.accessibility,
        response = keyboardNavigationHandler.response,
        i = chart.highlightedExportItemIx || 0;
      // Try to highlight prev item in list. Highlighting e.g.
      // separators will fail.
      while (i--) {
        if (chart.highlightExportItem(i)) {
          return response.success;
        }
      }
      // We failed, so wrap around or move to prev module
      if (a11yOptions.keyboardNavigation.wrapAround) {
        chart.highlightLastExportItem();
        return response.success;
      }
      return response.prev;
    },
    /**
     * @private
     * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
     * @return {number}
     * Response code
     */
    onKbdNext: function (keyboardNavigationHandler) {
      var chart = this.chart,
        a11yOptions = chart.options.accessibility,
        response = keyboardNavigationHandler.response,
        i = (chart.highlightedExportItemIx || 0) + 1;
      // Try to highlight next item in list. Highlighting e.g.
      // separators will fail.
      for (; i < chart.exportDivElements.length; ++i) {
        if (chart.highlightExportItem(i)) {
          return response.success;
        }
      }
      // We failed, so wrap around or move to next module
      if (a11yOptions.keyboardNavigation.wrapAround) {
        chart.highlightExportItem(0);
        return response.success;
      }
      return response.next;
    },
    /**
     * @private
     * @param {Highcharts.KeyboardNavigationHandler} keyboardNavigationHandler
     * @return {number}
     * Response code
     */
    onKbdClick: function (keyboardNavigationHandler) {
      var chart = this.chart;
      this.fakeClickEvent(
        chart.exportDivElements[chart.highlightedExportItemIx]
      );
      return keyboardNavigationHandler.response.success;
    },
  }
);
export default MenuComponent;
